package com.weilx959.spt.plugin.e2sp.model;

import com.weilx959.spt.plugin.e2sp.constant.Dialect;
import com.weilx959.spt.plugin.e2sp.constant.ExcelToSqlConstants;
import com.weilx959.spt.plugin.e2sp.model.statement.ErrorStatement;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.util.Objects;

/**
 * 转换excel vo
 *
 * @author weilx
 * @since 2024-05-02
 */
public class TransformedExcelVO {
    private StringProperty name;

    /**
     * 数据库方言
     */
    private ObjectProperty<Dialect> dialect;

    /**
     * 默认数据库schema
     */
    private StringProperty defaultSchema;

    /**
     * 输出目录
     */
    private StringProperty outputDir;

    /**
     * 是否生成delete语句
     */
    private BooleanProperty deleteInsert;

    /**
     * 是否生成check语句
     */
    private BooleanProperty check;

    /**
     * check语句模板
     */
    private StringProperty checkTemplate;

    private ObjectProperty<ObservableList<TableVO>> tables;

    private ObjectProperty<ObservableList<ErrorStatement>> errorStatements;

    private static final String S_NAME = "NAME";
    private static final String S_DIALECT = "DIALECT";
    private static final String S_DEFAULT_SCHEMA = "DEFAULT_SCHEMA";
    private static final String S_OUTPUT_DIR = "OUTPUT_DIR";
    private static final String S_DELETE_INSERT = "DELETE_INSERT";
    private static final String S_CHECK = "CHECK";
    private static final String S_CHECK_TEMPLATE = "CHECK_TEMPLATE";
    private static final String S_TABLES = "TABLES";
    private static final String S_ERROR_STATEMENTS = "ERROR_STATEMENTS";

    public TransformedExcelVO() {
        this.name = new SimpleStringProperty(this, S_NAME);
        this.dialect = new SimpleObjectProperty<>(this, S_DIALECT);
        this.defaultSchema = new SimpleStringProperty(this, S_DEFAULT_SCHEMA);
        this.outputDir = new SimpleStringProperty(this, S_OUTPUT_DIR);
        this.deleteInsert = new SimpleBooleanProperty(this, S_DELETE_INSERT, false);
        this.check = new SimpleBooleanProperty(this, S_CHECK, true);
        this.checkTemplate = new SimpleStringProperty(this, S_CHECK_TEMPLATE, ExcelToSqlConstants.DEFAULT_CHECK_SQL_TEMPLATE);
        this.tables = new SimpleObjectProperty<>(this, S_TABLES, FXCollections.observableArrayList());
        this.errorStatements = new SimpleObjectProperty<>(this, S_ERROR_STATEMENTS, FXCollections.observableArrayList());
    }

    public TransformedExcelVO(String name, Dialect dialect, String defaultSchema, String outputDir,
                              boolean deleteInsert, boolean check, String checkTemplate, ObservableList<TableVO> tables, ObservableList<ErrorStatement> errorStatements) {
        this.name = new SimpleStringProperty(this, S_NAME, name);
        this.dialect = new SimpleObjectProperty<>(this, S_DIALECT, dialect);
        this.defaultSchema = new SimpleStringProperty(this, S_DEFAULT_SCHEMA, defaultSchema);
        this.outputDir = new SimpleStringProperty(this, S_OUTPUT_DIR, outputDir);
        this.deleteInsert = new SimpleBooleanProperty(this, S_DELETE_INSERT, deleteInsert);
        this.check = new SimpleBooleanProperty(this, S_CHECK, check);
        this.checkTemplate = new SimpleStringProperty(this, S_CHECK_TEMPLATE, checkTemplate);
        this.tables = new SimpleObjectProperty<>(this, S_TABLES, tables);
        this.errorStatements = new SimpleObjectProperty<>(this, S_ERROR_STATEMENTS, errorStatements);
    }

    // getter and setter

    public String getName() {
        return this.nameProperty().get();
    }

    public void setName(String name) {
        this.nameProperty().set(name);
    }

    public StringProperty nameProperty() {
        if (Objects.isNull(name)) {
            this.name = new SimpleStringProperty(this, S_NAME);
        }
        return name;
    }

    public StringProperty nameProperty(String name) {
        this.nameProperty().set(name);
        return this.name;
    }

    public Dialect getDialect() {
        return this.dialectProperty().get();
    }

    public void setDialect(Dialect dialect) {
        this.dialectProperty().set(dialect);
    }

    public ObjectProperty<Dialect> dialectProperty() {
        if (Objects.isNull(dialect)) {
            this.dialect = new SimpleObjectProperty<>(this, S_DIALECT);
        }
        return dialect;
    }

    public ObjectProperty<Dialect> dialectProperty(Dialect dialect) {
        this.dialectProperty().set(dialect);
        return this.dialect;
    }

    public String getDefaultSchema() {
        return this.defaultSchemaProperty().get();
    }

    public void setDefaultSchema(String defaultSchema) {
        this.defaultSchemaProperty().set(defaultSchema);
    }

    public StringProperty defaultSchemaProperty() {
        if (Objects.isNull(defaultSchema)) {
            this.defaultSchema = new SimpleStringProperty(this, S_DEFAULT_SCHEMA);
        }
        return defaultSchema;
    }

    public StringProperty defaultSchemaProperty(String defaultSchema) {
        this.defaultSchemaProperty().set(defaultSchema);
        return this.defaultSchema;
    }

    public String getOutputDir() {
        return this.outputDirProperty().get();
    }

    public void setOutputDir(String outputDir) {
        this.outputDirProperty().set(outputDir);
    }

    public StringProperty outputDirProperty() {
        if (Objects.isNull(outputDir)) {
            this.outputDir = new SimpleStringProperty(this, S_OUTPUT_DIR);
        }
        return outputDir;
    }

    public StringProperty outputDirProperty(String outputDir) {
        this.outputDirProperty().set(outputDir);
        return this.outputDir;
    }

    public boolean isDeleteInsert() {
        return this.deleteInsertProperty().get();
    }

    public void setDeleteInsert(boolean deleteInsert) {
        this.deleteInsertProperty().set(deleteInsert);
    }

    public BooleanProperty deleteInsertProperty() {
        if (Objects.isNull(deleteInsert)) {
            this.deleteInsert = new SimpleBooleanProperty(this, S_DELETE_INSERT, false);
        }
        return deleteInsert;
    }

    public BooleanProperty deleteInsertProperty(boolean deleteInsert) {
        this.deleteInsertProperty().set(deleteInsert);
        return this.deleteInsert;
    }

    public boolean isCheck() {
        return this.checkProperty().get();
    }

    public void setCheck(boolean check) {
        this.checkProperty().set(check);
    }

    public BooleanProperty checkProperty() {
        if (Objects.isNull(check)) {
            this.check = new SimpleBooleanProperty(this, S_CHECK, true);
        }
        return check;
    }

    public BooleanProperty checkProperty(boolean check) {
        this.checkProperty().set(check);
        return this.check;
    }

    public String getCheckTemplate() {
        return this.checkTemplateProperty().get();
    }

    public void setCheckTemplate(String checkTemplate) {
        this.checkTemplateProperty().set(checkTemplate);
    }

    public StringProperty checkTemplateProperty() {
        if (Objects.isNull(checkTemplate)) {
            this.checkTemplate = new SimpleStringProperty(this, S_CHECK_TEMPLATE, ExcelToSqlConstants.DEFAULT_CHECK_SQL_TEMPLATE);
        }
        return checkTemplate;
    }

    public StringProperty checkTemplateProperty(String checkTemplate) {
        this.checkTemplateProperty().set(checkTemplate);
        return this.checkTemplate;
    }

    public ObservableList<TableVO> getTables() {
        return this.tablesProperty().get();
    }

    public void setTables(ObservableList<TableVO> tables) {
        this.tablesProperty().set(tables);
    }

    public ObjectProperty<ObservableList<TableVO>> tablesProperty() {
        if (Objects.isNull(tables)) {
            this.tables = new SimpleObjectProperty<>(this, S_TABLES, FXCollections.observableArrayList());
        }
        return tables;
    }

    public ObjectProperty<ObservableList<TableVO>> tablesProperty(ObservableList<TableVO> tables) {
        this.tablesProperty().set(tables);
        return this.tables;
    }

    public ObservableList<ErrorStatement> getErrorStatements() {
        return this.errorStatementsProperty().get();
    }

    public void setErrorStatements(ObservableList<ErrorStatement> errorStatements) {
        this.errorStatementsProperty().set(errorStatements);
    }

    public ObjectProperty<ObservableList<ErrorStatement>> errorStatementsProperty() {
        if (Objects.isNull(errorStatements)) {
            this.errorStatements = new SimpleObjectProperty<>(this, S_ERROR_STATEMENTS, FXCollections.observableArrayList());
        }
        return errorStatements;
    }

    // builder

    public static TransformedExcelVOBuilder builder() {
        return new TransformedExcelVOBuilder();
    }

    public static class TransformedExcelVOBuilder {
        private String name;
        private Dialect dialect;
        private String defaultSchema;
        private String outputDir;
        private boolean deleteInsert;
        private boolean check;
        private String checkTemplate;
        private ObservableList<TableVO> tables;
        private ObservableList<ErrorStatement> errorStatements;

        public TransformedExcelVOBuilder name(String name) {
            this.name = name;
            return this;
        }

        public TransformedExcelVOBuilder dialect(Dialect dialect) {
            this.dialect = dialect;
            return this;
        }

        public TransformedExcelVOBuilder defaultSchema(String defaultSchema) {
            this.defaultSchema = defaultSchema;
            return this;
        }

        public TransformedExcelVOBuilder outputDir(String outputDir) {
            this.outputDir = outputDir;
            return this;
        }

        public TransformedExcelVOBuilder deleteInsert(boolean delete) {
            this.deleteInsert = delete;
            return this;
        }

        public TransformedExcelVOBuilder check(boolean check) {
            this.check = check;
            return this;
        }

        public TransformedExcelVOBuilder checkTemplate(String checkTemplate) {
            this.checkTemplate = checkTemplate;
            return this;
        }

        public TransformedExcelVOBuilder tables(ObservableList<TableVO> tables) {
            this.tables = tables;
            return this;
        }

        public TransformedExcelVOBuilder errorStatements(ObservableList<ErrorStatement> errorStatements) {
            this.errorStatements = errorStatements;
            return this;
        }

        public TransformedExcelVO build() {
            if (Objects.isNull(tables)) {
                tables = FXCollections.observableArrayList();
            }
            if (Objects.isNull(errorStatements)) {
                errorStatements = FXCollections.observableArrayList();
            }
            return new TransformedExcelVO(name, dialect, defaultSchema, outputDir, deleteInsert, check, checkTemplate, tables, errorStatements);
        }
    }

    /**
     * 表格 VO
     */
    public static class TableVO {
        /**
         * 数据库schema
         */
        private StringProperty schema;

        /**
         * 表名
         */
        private StringProperty name;

        private IntegerProperty index;

        /**
         * excel sheet名
         */
        private StringProperty sheetName;

        private ObjectProperty<ObservableList<RecordVO>> recordList;

        private static final String S_NAME = "NAME";
        private static final String S_SCHEMA = "SCHEMA";
        private static final String S_INDEX = "INDEX";
        private static final String S_SHEET_NAME = "SHEET_NAME";
        private static final String S_RECORD_LIST = "RECORD_LIST";

        public TableVO() {
            this.schema = new SimpleStringProperty(this, S_SCHEMA);
            this.name = new SimpleStringProperty(this, S_NAME);
            this.index = new SimpleIntegerProperty(this, S_INDEX, -1);
            this.sheetName = new SimpleStringProperty(this, S_SHEET_NAME);
            this.recordList = new SimpleObjectProperty<>(this, S_RECORD_LIST, FXCollections.observableArrayList());
        }

        public TableVO(String schema, String name, int index, String sheetName, ObservableList<RecordVO> recordList) {
            this.schema = new SimpleStringProperty(this, S_SCHEMA, schema);
            this.name = new SimpleStringProperty(this, S_NAME, name);
            this.index = new SimpleIntegerProperty(this, S_INDEX, index);
            this.sheetName = new SimpleStringProperty(this, S_SHEET_NAME, sheetName);
            this.recordList = new SimpleObjectProperty<>(this, S_RECORD_LIST, recordList);
        }

        // getter and setter

        public String getSchema() {
            return this.schemaProperty().get();
        }

        public void setSchema(String schema) {
            this.schemaProperty().set(schema);
        }

        public StringProperty schemaProperty() {
            if (Objects.isNull(schema)) {
                this.schema = new SimpleStringProperty(this, S_SCHEMA);
            }
            return schema;
        }

        public StringProperty schemaProperty(String schema) {
            this.schemaProperty().set(schema);
            return this.schema;
        }

        public String getName() {
            return this.nameProperty().get();
        }

        public void setName(String name) {
            this.nameProperty().set(name);
        }

        public StringProperty nameProperty() {
            if (Objects.isNull(name)) {
                this.name = new SimpleStringProperty(this, S_NAME);
            }
            return name;
        }

        public StringProperty nameProperty(String name) {
            this.nameProperty().set(name);
            return this.name;
        }

        public int getIndex() {
            return this.indexProperty().get();
        }

        public void setIndex(int index) {
            this.indexProperty().set(index);
        }

        public IntegerProperty indexProperty() {
            if (Objects.isNull(index)) {
                this.index = new SimpleIntegerProperty(this, S_INDEX, -1);
            }
            return index;
        }

        public IntegerProperty indexProperty(int index) {
            this.indexProperty().set(index);
            return this.index;
        }

        public String getSheetName() {
            return this.sheetNameProperty().get();
        }

        public void setSheetName(String sheetName) {
            this.sheetNameProperty().set(sheetName);
        }

        public StringProperty sheetNameProperty() {
            if (Objects.isNull(sheetName)) {
                this.sheetName = new SimpleStringProperty(this, S_SHEET_NAME);
            }
            return sheetName;
        }

        public StringProperty sheetNameProperty(String sheetName) {
            this.sheetNameProperty().set(sheetName);
            return this.sheetName;
        }

        public ObservableList<RecordVO> getRecordList() {
            return this.recordListProperty().get();
        }

        public void setRecordList(ObservableList<RecordVO> recordList) {
            this.recordListProperty().set(recordList);
        }

        public ObjectProperty<ObservableList<RecordVO>> recordListProperty() {
            if (Objects.isNull(recordList)) {
                this.recordList = new SimpleObjectProperty<>(this, S_RECORD_LIST, FXCollections.observableArrayList());
            }
            return recordList;
        }

        public ObjectProperty<ObservableList<RecordVO>> recordListProperty(ObservableList<RecordVO> recordList) {
            this.recordListProperty().set(recordList);
            return this.recordList;
        }

        // builder

        public static TableVOBuilder builder() {
            return new TableVOBuilder();
        }

        public static class TableVOBuilder {
            private String schema;
            private String name;
            private int index;
            private String sheetName;
            private ObservableList<RecordVO> recordList;

            public TableVOBuilder schema(String schema) {
                this.schema = schema;
                return this;
            }

            public TableVOBuilder name(String name) {
                this.name = name;
                return this;
            }

            public TableVOBuilder index(int index) {
                this.index = index;
                return this;
            }

            public TableVOBuilder sheetName(String sheetName) {
                this.sheetName = sheetName;
                return this;
            }

            public TableVOBuilder recordList(ObservableList<RecordVO> recordList) {
                this.recordList = recordList;
                return this;
            }

            public TableVO build() {
                if (Objects.isNull(recordList)) {
                    recordList = FXCollections.observableArrayList();
                }
                return new TableVO(schema, name, index, sheetName, recordList);
            }
        }
    }

    /**
     * 表格数据记录 VO
     */
    public static class RecordVO {
        /**
         * 行索引
         */
        private IntegerProperty index;

        /**
         * 主键列表
         */
        private ObjectProperty<ObservableList<String>> keys;

        /**
         * 全部列名列表
         */
        private ObjectProperty<ObservableList<String>> columns;

        /**
         * 数据值列表
         */
        private ObjectProperty<ObservableList<Object>> values;

        private static final String S_INDEX = "INDEX";
        private static final String S_KEYS = "KEYS";
        private static final String S_COLUMNS = "COLUMNS";
        private static final String S_VALUES = "VALUES";

        public RecordVO() {
            this.index = new SimpleIntegerProperty(this, S_INDEX, -1);
            this.keys = new SimpleObjectProperty<>(this, S_KEYS, FXCollections.observableArrayList());
            this.columns = new SimpleObjectProperty<>(this, S_COLUMNS, FXCollections.observableArrayList());
            this.values = new SimpleObjectProperty<>(this, S_VALUES, FXCollections.observableArrayList());
        }

        public RecordVO(int index, ObservableList<String> keys, ObservableList<String> columns, ObservableList<Object> values) {
            this.index = new SimpleIntegerProperty(this, S_INDEX, index);
            this.keys = new SimpleObjectProperty<>(this, S_KEYS, keys);
            this.columns = new SimpleObjectProperty<>(this, S_COLUMNS, columns);
            this.values = new SimpleObjectProperty<>(this, S_VALUES, values);
        }

        // getter and setter

        public int getIndex() {
            return this.indexProperty().get();
        }

        public void setIndex(int index) {
            this.indexProperty().set(index);
        }

        public IntegerProperty indexProperty() {
            if (Objects.isNull(index)) {
                this.index = new SimpleIntegerProperty(this, S_INDEX, -1);
            }
            return index;
        }

        public IntegerProperty indexProperty(int index) {
            this.indexProperty().set(index);
            return this.index;
        }

        public ObservableList<String> getKeys() {
            return this.keysProperty().get();
        }

        public void setKeys(ObservableList<String> keys) {
            this.keysProperty().set(keys);
        }

        public ObjectProperty<ObservableList<String>> keysProperty() {
            if (Objects.isNull(keys)) {
                this.keys = new SimpleObjectProperty<>(this, S_KEYS, FXCollections.observableArrayList());
            }
            return keys;
        }

        public ObjectProperty<ObservableList<String>> keysProperty(ObservableList<String> keys) {
            this.keysProperty().set(keys);
            return this.keys;
        }

        public ObservableList<String> getColumns() {
            return this.columnsProperty().get();
        }

        public void setColumns(ObservableList<String> columns) {
            this.columnsProperty().set(columns);
        }

        public ObjectProperty<ObservableList<String>> columnsProperty() {
            if (Objects.isNull(columns)) {
                this.columns = new SimpleObjectProperty<>(this, S_COLUMNS, FXCollections.observableArrayList());
            }
            return columns;
        }

        public ObjectProperty<ObservableList<String>> columnsProperty(ObservableList<String> columns) {
            this.columnsProperty().set(columns);
            return this.columns;
        }

        public ObservableList<Object> getValues() {
            return this.valuesProperty().get();
        }

        public void setValues(ObservableList<Object> values) {
            this.valuesProperty().set(values);
        }

        public ObjectProperty<ObservableList<Object>> valuesProperty() {
            if (Objects.isNull(values)) {
                this.values = new SimpleObjectProperty<>(this, S_VALUES, FXCollections.observableArrayList());
            }
            return values;
        }

        public ObjectProperty<ObservableList<Object>> valuesProperty(ObservableList<Object> values) {
            this.valuesProperty().set(values);
            return this.values;
        }

        // builder

        public static RecordVOBuilder builder() {
            return new RecordVOBuilder();
        }

        public static class RecordVOBuilder {
            private int index;
            private ObservableList<String> keys;
            private ObservableList<String> columns;
            private ObservableList<Object> values;

            public RecordVOBuilder index(int index) {
                this.index = index;
                return this;
            }

            public RecordVOBuilder keys(ObservableList<String> keys) {
                this.keys = keys;
                return this;
            }

            public RecordVOBuilder columns(ObservableList<String> columns) {
                this.columns = columns;
                return this;
            }

            public RecordVOBuilder values(ObservableList<Object> values) {
                this.values = values;
                return this;
            }

            public RecordVO build() {
                if (Objects.isNull(keys)) {
                    keys = FXCollections.observableArrayList();
                }
                if (Objects.isNull(columns)) {
                    columns = FXCollections.observableArrayList();
                }
                if (Objects.isNull(values)) {
                    values = FXCollections.observableArrayList();
                }
                return new RecordVO(index, keys, columns, values);
            }
        }
    }
}
